﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Runtime.Serialization;
using Vit.Common.Helpers;

namespace Vit.Common
{

    public class Assert
    {
        public static Assert With(object obj)
        {
            return new Assert(obj.GetType());
        }

        public static Assert With(Type type)
        {
            return new Assert(type);
        }

        public static Assert With<T>()
        {
            return new Assert(typeof(T));
        }

        private List<object> parameters;
        private string message;
        private bool result = true;
        private Type objType = typeof(Assert);
        private Exception exception;

        public Assert(Type type)
        {
            parameters = new List<object>();
            this.objType = type;
        }

        public Assert Parameters(object para)
        {
            parameters.Add(para);
            return this;
        }

        public Assert Message(string message)
        {
            this.message = message;
            return this;
        }

        public Assert Condition(bool condition)
        {
            this.result = this.result && condition;
            return this;
        }

        public Assert Condition(Func<bool> func)
        {
            this.result = this.result && func();
            return this;
        }

        public Assert Condition<T>(T obj, Predicate<T> func)
        {
            this.result = this.result && func(obj);
            return this;
        }

        public Assert Exception(Exception ex)
        {
            this.exception = ex;
            return this;
        }

        public void Check()
        {
            if (result)
                return;
            if (string.IsNullOrEmpty(message) && parameters.Count > 0)
                message = "parameter_cannot_be_null";
            message = Locale.GetString(objType, message);
            string error = string.Format(message, parameters.ToArray());
            throw new AssertFailedException(error, exception);
        }
    }

    public static class AssertHelperExtension
    {
        public static void AreEqual(this Assert assert, object expected, object actual)
        {
            assert.Condition(expected.Equals(actual)).Check();
        }

        public static void AreNotEqual(this Assert assert, object notExpected, object actual)
        {
            assert.Condition(!notExpected.Equals(actual)).Check();
        }

        public static void AreSame(this Assert assert, object expected, object actual)
        {
            assert.Condition(object.ReferenceEquals(expected, actual)).Check();
        }

        public static void AreNotSame(this Assert assert, object notExpected, object actual)
        {
            assert.Condition(!object.ReferenceEquals(notExpected, actual)).Check();
        }

        public static void IsTrue(this Assert assert, bool condition)
        {
            assert.Condition(condition).Check();
        }

        public static void IsFalse(this Assert assert, bool condition)
        {
            assert.Condition(!condition).Check();
        }

        public static void IsInstanceOfType(this Assert assert, object value, Type expectedType)
        {
            assert.Condition(expectedType.IsInstanceOfType(value)).Check();
        }

        public static void IsNotInstanceOfType(this Assert assert, object value, Type wrongType)
        {
            assert.Condition(!wrongType.IsInstanceOfType(value)).Check();
        }

        public static void IsNull(this Assert assert, object value)
        {
            assert.Condition(value == null).Check();
        }

        public static void IsNotNull(this Assert assert, object value)
        {
            assert.Condition(value != null).Check();
        }

        public static void IsStringEmptyOrNull(this Assert assert, string value)
        {
            assert.Condition(!string.IsNullOrEmpty(value)).Check();
        }

        public static void IsStringNotEmptyOrNull(this Assert assert, string value)
        {
            assert.Condition(string.IsNullOrEmpty(value)).Check();
        }

        public static void EnumValueIsDefined(this Assert assert, Type enumType, object value, string argumentName)
        {
            assert.Condition(Enum.IsDefined(enumType, value)).Check();
        }

        public static void TypeIsAssignableFromType(this Assert assert, Type assignee, Type providedType, string argumentName)
        {
            assert.Condition(providedType.IsAssignableFrom(assignee)).Check();
        }
    }

    /// <summary>
    /// 断言失败异常
    /// </summary>
    [Serializable]
    public class AssertFailedException : Exception
    {
        public AssertFailedException()
        {
        }

        public AssertFailedException(string msg)
            : base(msg)
        {
        }

        protected AssertFailedException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        public AssertFailedException(string msg, Exception ex)
            : base(msg, ex)
        {
        }
    }
}
